package bridge

import (
	"fmt"
	"log"
	"sync"
	"time"
	"ebbflow/pkg/adkr"
	"ebbflow/src/message"
)

// EbbFlow represents the integrated protocol
type EbbFlow struct {
	// Bridge state
	nodeID       int
	totalNodes   int
	faultyNodes  int
	isActive     bool
	
	// Communication channels
	waterbearInCh  chan message.ReplicaMessage
	waterbearOutCh chan message.ReplicaMessage
	adkrInCh       chan adkr.Message
	adkrOutCh      chan adkr.Message
	
	// Synchronization
	mu sync.RWMutex
	
	// Protocol state
	currentEpoch int
	consensusResults map[int][]byte
}

// NewEbbFlow creates a new integrated protocol instance
func NewEbbFlow(nodeID, totalNodes, faultyNodes int) *EbbFlow {
	return &EbbFlow{
		nodeID:           nodeID,
		totalNodes:       totalNodes,
		faultyNodes:     faultyNodes,
		isActive:        false,
		waterbearInCh:   make(chan message.ReplicaMessage, 1000),
		waterbearOutCh:  make(chan message.ReplicaMessage, 1000),
		adkrInCh:        make(chan adkr.Message, 1000),
		adkrOutCh:       make(chan adkr.Message, 1000),
		currentEpoch:    0,
		consensusResults: make(map[int][]byte),
	}
}

// Start initializes and starts the integrated protocol
func (a *EbbFlow) Start() error {
	a.mu.Lock()
	defer a.mu.Unlock()
	
	if a.isActive {
		return fmt.Errorf("protocol is already active")
	}
	
	log.Printf("Starting EbbFlow for node %d", a.nodeID)
	
	// Start protocol coordination
	go a.coordinateProtocols()
	
	a.isActive = true
	return nil
}

// coordinateProtocols manages the interaction between WaterBear and ADKR
func (a *EbbFlow) coordinateProtocols() {
	log.Printf("Starting protocol coordination for node %d", a.nodeID)
	
	for {
		select {
		case wbMsg := <-a.waterbearOutCh:
			// Convert WaterBear message to ADKR format
			adkrMsg := a.convertWaterBearToADKR(wbMsg)
			if adkrMsg != nil {
				a.adkrOutCh <- *adkrMsg
			}
			
		case adkrMsg := <-a.adkrOutCh:
			// Convert ADKR message to WaterBear format
			wbMsg := a.convertADKRToWaterBear(adkrMsg)
			if wbMsg != nil {
				a.waterbearOutCh <- *wbMsg
			}
			
		case <-time.After(100 * time.Millisecond):
			// Periodic coordination tasks
			a.performPeriodicCoordination()
		}
	}
}

// convertWaterBearToADKR converts WaterBear messages to ADKR format
func (a *EbbFlow) convertWaterBearToADKR(wbMsg message.ReplicaMessage) *adkr.Message {
	// Map WaterBear message types to ADKR message types
	var adkrTag adkr.BroadcastTag
	
	switch wbMsg.Mtype {
	case message.RBC_SEND:
		adkrTag = adkr.ADKG_RBC
	case message.ABA_BVAL:
		adkrTag = adkr.ADKR_ABA
	case message.ABA_AUX:
		adkrTag = adkr.ADKR_ABA
	case message.ABA_CONF:
		adkrTag = adkr.ADKR_ABA
	case message.ABA_FINAL:
		adkrTag = adkr.ADKR_ABA
	default:
		return nil
	}
	
	// Serialize WaterBear message as ADKR payload
	payload, err := wbMsg.Serialize()
	if err != nil {
		log.Printf("Error serializing WaterBear message: %v", err)
		return nil
	}
	
	return &adkr.Message{
		Tag:     adkrTag,
		Round:   wbMsg.Round,
		Sender:  int(wbMsg.Source),
		To:      -1, // Broadcast
		Payload: payload,
	}
}

// convertADKRToWaterBear converts ADKR messages to WaterBear format
func (a *EbbFlow) convertADKRToWaterBear(adkrMsg adkr.Message) *message.ReplicaMessage {
	// Map ADKR message types to WaterBear message types
	var wbMtype message.TypeOfMessage
	
	switch adkrMsg.Tag {
	case adkr.ADKG_RBC:
		wbMtype = message.RBC_SEND
	case adkr.ADKR_ABA:
		wbMtype = message.ABA_BVAL
	case adkr.ADKR_MVBA:
		wbMtype = message.RBC_READY
	default:
		return nil
	}
	
	// Deserialize ADKR payload as WaterBear message
	wbMsg := message.DeserializeReplicaMessage(adkrMsg.Payload)
	
	// Update message fields
	wbMsg.Mtype = wbMtype
	wbMsg.Source = int64(adkrMsg.Sender)
	wbMsg.Round = adkrMsg.Round
	
	return &wbMsg
}

// performPeriodicCoordination handles periodic coordination tasks
func (a *EbbFlow) performPeriodicCoordination() {
	// Check if we need to trigger ADKR for dynamic reconfiguration
	if a.shouldTriggerADKR() {
		a.triggerADKRReconfiguration()
	}
	
	// Synchronize epoch information
	a.synchronizeEpochs()
}

// shouldTriggerADKR determines if ADKR should be triggered
func (a *EbbFlow) shouldTriggerADKR() bool {
	// Trigger ADKR when:
	// 1. Network conditions change significantly
	// 2. Node failures are detected
	// 3. Performance degrades below threshold
	// 4. Manual reconfiguration is requested
	
	// For now, trigger every 10 epochs
	return a.currentEpoch%10 == 0
}

// triggerADKRReconfiguration triggers ADKR for dynamic reconfiguration
func (a *EbbFlow) triggerADKRReconfiguration() {
	log.Printf("Triggering ADKR reconfiguration at epoch %d", a.currentEpoch)
	
	// Send reconfiguration request to ADKR
	reconfigMsg := adkr.Message{
		Tag:     adkr.ADKR_MVBA,
		Round:   a.currentEpoch,
		Sender:  a.nodeID,
		To:      -1,
		Payload: []byte(fmt.Sprintf("reconfig_epoch_%d", a.currentEpoch)),
	}
	
	a.adkrOutCh <- reconfigMsg
}

// synchronizeEpochs synchronizes epoch information between protocols
func (a *EbbFlow) synchronizeEpochs() {
	// Update current epoch based on WaterBear consensus
	// This ensures both protocols are synchronized
}

// SubmitTransaction submits a transaction to the integrated protocol
func (a *EbbFlow) SubmitTransaction(tx []byte) error {
	if !a.isActive {
		return fmt.Errorf("protocol is not active")
	}
	
	// Submit to WaterBear consensus first
	wbMsg := message.ReplicaMessage{
		Mtype:    message.RBC_SEND,
		Instance: a.currentEpoch,
		Source:   int64(a.nodeID),
		TS:       time.Now().UnixNano(),
		Payload:  tx,
		Epoch:    a.currentEpoch,
	}
	
	a.waterbearInCh <- wbMsg
	return nil
}

// GetConsensusResult retrieves consensus results
func (a *EbbFlow) GetConsensusResult(epoch int) ([]byte, bool) {
	a.mu.RLock()
	defer a.mu.RUnlock()
	
	result, exists := a.consensusResults[epoch]
	return result, exists
}

// Stop stops the integrated protocol
func (a *EbbFlow) Stop() {
	a.mu.Lock()
	defer a.mu.Unlock()
	
	if !a.isActive {
		return
	}
	
	log.Printf("Stopping EbbFlow for node %d", a.nodeID)
	
	// Close channels
	close(a.waterbearInCh)
	close(a.waterbearOutCh)
	close(a.adkrInCh)
	close(a.adkrOutCh)
	
	a.isActive = false
}

// GetProtocolStatus returns the current status of the integrated protocol
func (a *EbbFlow) GetProtocolStatus() map[string]interface{} {
	a.mu.RLock()
	defer a.mu.RUnlock()
	
	return map[string]interface{}{
		"node_id":        a.nodeID,
		"total_nodes":    a.totalNodes,
		"faulty_nodes":   a.faultyNodes,
		"is_active":      a.isActive,
		"current_epoch":  a.currentEpoch,
		"ebbflow/src/consensus_count": len(a.consensusResults),
	}
}
